package com.hj.springframework3.io;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * @Author : hujing
 * @Date: 2025/7/3 14:13
 * @Description: 资源解析器, 获取所有的class文件
 */
public class ResourceResolver {

    Logger logger = LoggerFactory.getLogger(getClass());

    // 解析的基础包路径
    String basePackage;

    public ResourceResolver(String basePackage) {
        this.basePackage = basePackage;
    }

    public <R> List<R> scan(Function<Resource, R> mapper) {
        // 包名转成路径名，例如：com.itranswarp.scan -> com/itranswarp/scan
        String basePackagePath = basePackage.replace(".", "/");
        ArrayList<R> collector = new ArrayList<>();
        scan0(basePackagePath, collector, mapper);
        return collector;
    }

    private <R> void scan0(String basePackagePath, ArrayList<R> collector, Function<Resource, R> mapper) {
        logger.info("扫描路径:{}", basePackagePath);
        // 获取类加载器
        ClassLoader classLoader = getContextClassLoader();
        // 获取类加载器下的资源
        try {
            Enumeration<URL> en = classLoader.getResources(basePackagePath);
            while (en.hasMoreElements()) {
                URL url = en.nextElement();
                // uir是不带ip,port的url
                URI uri = url.toURI();
                //uriStr = file:/F:/project/summer-framework/step-by-step/resource-resolver/target/test-classes/com/itranswarp/scan
                //basePackagePath = com/itranswarp/scan
                //uriBaseStr = file:/F:/project/summer-framework/step-by-step/resource-resolver/target/test-classes
                String uriStr = removeTrailingSlash(uriToString(uri));
                String uriBaseStr = uriStr.substring(0, uriStr.length() - basePackagePath.length());
                if (uriBaseStr.startsWith("file:")) {
                    uriBaseStr = uriBaseStr.substring(5);
                }
                if (uriBaseStr.startsWith("jar:")) {
                    scanFile(true, uriBaseStr, jarUriToPath(basePackagePath, uri), collector, mapper);
                } else {
                    scanFile(false, uriBaseStr, Paths.get(uri), collector, mapper);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将jar uri 转换为文件系统路径
     *
     * @param basePackagePath 扫描路径
     * @param jarUri          jar包的uri
     * @return
     * @throws IOException
     */
    Path jarUriToPath(String basePackagePath, URI jarUri) throws IOException {
        //FileSystems.newFileSystem() 打开 JAR 文件系统
        return FileSystems.newFileSystem(jarUri, Map.of()).getPath(basePackagePath);
    }

    /**
     * 根据mapper函数式接口对文件进行处理,并将结果添加到collector中
     * @param isJar 是否是jar包
     * @param base /F:/project/summer-framework/step-by-step/resource-resolver/target/test-classes/
     * @param root  F:\project\spring-parent\spring-framework3\target\test-classes\com\hj\springframework3\scan
     * @param collector
     * @param mapper
     * @param <R>
     * @throws IOException
     */
    <R> void scanFile(boolean isJar, String base, Path root, List<R> collector, Function<Resource, R> mapper) throws IOException {
        //baseDir:/F:/project/summer-framework/step-by-step/resource-resolver/target/test-classes
        String baseDir = removeTrailingSlash(base);
        //Files::isRegularFile:仅保留普通文件（排除目录、符号链接等）
        Files.walk(root).filter(Files::isRegularFile).forEach(filePath -> {
            Resource res;
            if (isJar) {
                //filePath.toString() 返回 JAR 内部路径（如 "com/example/MyClass.class"）。
                res = new Resource(baseDir, removeLeadingSlash(filePath.toString()));
            } else {
                //path:F:\project\summer-framework\step-by-step\resource-resolver\target\test-classes\com\itranswarp\scan\convert\ValueConverterBean.class
                String path = filePath.toString();
                //name:com\itranswarp\scan\convert\ValueConverterBean.class，即包名+类名
                String name = removeLeadingSlash(path.substring(baseDir.length()));
                res = new Resource("file:" + path, name);
            }
            logger.info("found resource:{}" + res);
            R r = mapper.apply(res);
            if (r != null) {
                collector.add(r);
            }
        });
    }

    /**
     * 去掉路径末尾的斜杠
     *
     * @param s
     * @return
     */
    private String removeTrailingSlash(String s) {
        if (s.endsWith("/") || s.endsWith("\\")) {
            s = s.substring(0, s.length() - 1);
        }
        return s;
    }

    /**
     * 去掉开头的斜杠
     *
     * @param s
     * @return
     */
    private String removeLeadingSlash(String s) {
        if (s.startsWith("/") || s.startsWith("\\")) {
            s = s.substring(1);
        }
        return s;
    }

    private String uriToString(URI uri) {
        return URLDecoder.decode(uri.toString(), StandardCharsets.UTF_8);
    }

    /**
     * ClassLoader首先从Thread.getContextClassLoader()获取，如果获取不到，再从当前Class获取，因为Web应用的ClassLoader不是JVM提供的基于Classpath的ClassLoader，
     * 而是Servlet容器提供的ClassLoader，它不在默认的Classpath搜索，而是在/WEB-INF/classes目录和/WEB-INF/lib的所有jar包搜索，
     * 从Thread.getContextClassLoader()可以获取到Servlet容器专属的ClassLoader；
     *
     * @return
     */
    ClassLoader getContextClassLoader() {
        ClassLoader cl;
        cl = Thread.currentThread().getContextClassLoader();
        if (cl == null) {
            cl = getClass().getClassLoader();
        }
        return cl;
    }
}
